package com.fa4j.system.settings.listener;

import com.fa4j.common.base.model.BaseEnum;
import com.fa4j.common.base.model.ModuleInfo;
import com.fa4j.common.web.model.AppApi;
import com.fa4j.common.web.model.AppType;
import com.fa4j.system.settings.DictClient;
import com.fa4j.system.settings.dto.SystemDictBatchSaveRequest;
import com.fa4j.system.settings.model.SettingsProps;
import com.google.common.base.CaseFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class DictReportListener implements ApplicationListener<ApplicationReadyEvent> {

    private static final String DICT_CODE_MODULE = "MODULE";
    private static final String DICT_CODE_MODULE_API = "MODULE_API";

    private static final Map<String, Integer> HTTP_METHOD_SORT_MAP = new HashMap<>() {{
        put("GET", 1);
        put("POST", 2);
        put("PUT", 3);
        put("PATCH", 4);
        put("DELETE", 5);
    }};

    private final DictClient dictClient;
    private final List<AppApi> apiList;
    private final List<ModuleInfo> moduleInfoList;

    private final SettingsProps settingsProps;

    /**
     * 上报ApiList
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        if (!settingsProps.isEnableReport()) {
            return;
        }
        long start = System.currentTimeMillis();
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        futures.add(runAsync("模块", this::reportModule));
        Map<String, ModuleInfo> moduleInfoMap = new HashMap<>();
        List<String> firstModuleNames = new ArrayList<>();//一级模块
        for (ModuleInfo moduleInfo : moduleInfoList) {
            moduleInfoMap.put(moduleInfo.getName(), moduleInfo);
            if (!moduleInfo.getName().contains(".")) {
                firstModuleNames.add(moduleInfo.getName());
            }
            for (Class<? extends BaseEnum> baseEnum : moduleInfo.getEnumClassList()) {
                futures.add(runAsync("枚举" + baseEnum.getSimpleName(), () -> reportModuleEnum(moduleInfo, baseEnum)));
            }
        }
        // 上报应用API
        for (String firstModuleName : firstModuleNames) {
            for (AppType appType : AppType.values()) {
                ModuleInfo moduleInfo = moduleInfoMap.get(firstModuleName);
                futures.add(runAsync("模块API-" + moduleInfo.getDescription() + "-" + appType.getName(),
                        () -> reportModuleApi(
                                appType,
                                moduleInfo,
                                apiList.stream().filter(api ->
                                        Arrays.asList(api.getAppTypes()).contains(appType)
                                                && api.getModuleName().startsWith(moduleInfo.getName())
                                ).toList(),
                                moduleInfoMap
                        )
                ));
            }
        }
        futures.forEach(CompletableFuture::join);
        long end = System.currentTimeMillis();
        log.info("执行上报任务完成,任务数量:{},耗时:{}ms", futures.size(), (end - start));

    }

    private SystemDictBatchSaveRequest reportModuleEnum(ModuleInfo moduleInfo, Class<? extends BaseEnum> baseEnum) {
        String rootDictCode = "ENUM_" + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, baseEnum.getSimpleName());
        List<SystemDictBatchSaveRequest.Item> list = new ArrayList<>();
        for (BaseEnum item : baseEnum.getEnumConstants()) {
            Map<String, Object> customData = new HashMap<>();
            customData.put("moduleName", moduleInfo.getName());
            list.add(new SystemDictBatchSaveRequest.Item(
                    item.getName(),
                    ((Enum<?>) item).name(),
                    null,
                    item.getCode(),
                    rootDictCode,
                    customData
            ));
        }
        SystemDictBatchSaveRequest request = new SystemDictBatchSaveRequest();
        request.setRootCode(rootDictCode);
        Schema schema = baseEnum.getAnnotation(Schema.class);
        request.setRootName(schema == null ? baseEnum.getSimpleName() : schema.description());
        request.setValueType("CUSTOM");
        request.setMaxLevel(2);
        request.setCustomFields(List.of(
                new SystemDictBatchSaveRequest.DictCustomField("moduleName", "模块名", "STRING", false)
        ));
        request.setItems(list);
        return request;
    }

    private SystemDictBatchSaveRequest reportModuleApi(AppType appType, ModuleInfo moduleInfo, List<AppApi> apiList, Map<String, ModuleInfo> moduleInfoMap) {
        String rootCode = DICT_CODE_MODULE_API + "_" + moduleInfo.getName().toUpperCase() + "_" + appType.name();
        List<SystemDictBatchSaveRequest.Item> list = new ArrayList<>();

        apiList.stream().collect(Collectors.groupingBy(AppApi::getModuleCode))
                .entrySet()
                .stream().sorted(Map.Entry.comparingByKey())
                .forEach(entry -> {
                    entry.getValue()
                            .sort(
                                    Comparator.comparing(getAppApiStringFunction())
                                            .thenComparing(api -> HTTP_METHOD_SORT_MAP.getOrDefault(api.getHttpMethod(), 10))
                                            .thenComparing(AppApi::getHttpPath)
                            );
                    for (int i = 0; i < entry.getValue().size(); i++) {
                        AppApi appApi = entry.getValue().get(i);
                        Map<String, Object> customData = new HashMap<>();
                        customData.put("moduleName", appApi.getModuleName());
                        list.add(new SystemDictBatchSaveRequest.Item(
                                appApi.getName(),
                                appApi.getHttpMethod() + ":" + appApi.getHttpPath(),
                                null,
                                moduleInfoMap.get(appApi.getModuleName()).getCode() * 100 + i + 1,
                                rootCode,
                                customData
                        ));
                    }
                });


        SystemDictBatchSaveRequest request = new SystemDictBatchSaveRequest();
        request.setRootCode(rootCode);
        request.setRootName(moduleInfo.getDescription() + appType.getName() + "接口");
        request.setValueType("CUSTOM");
        request.setMaxLevel(2);
        request.setCustomFields(List.of(
                new SystemDictBatchSaveRequest.DictCustomField("moduleName", "模块名", "STRING", false)
        ));
        request.setItems(list);
        return request;
    }

    private static Function<AppApi, String> getAppApiStringFunction() {
        return api -> api.getName().split("-")[0];
    }


    private SystemDictBatchSaveRequest reportModule() {
        List<SystemDictBatchSaveRequest.Item> list = new ArrayList<>();
        for (ModuleInfo moduleInfo : moduleInfoList) {
            Map<String, Object> customData = new HashMap<>();
            customData.put("type", moduleInfo.getType().name());
            customData.put("code", moduleInfo.getCode());
            customData.put("packageName", moduleInfo.getPackageName());
            customData.put("packageVersion", moduleInfo.getPackageVersion());
            int parentIndex = moduleInfo.getName().lastIndexOf(".");
            String parentCode = parentIndex > 0 ? moduleInfo.getName().substring(0, parentIndex) : "";
            String finalParentCode = parentCode;
            if (moduleInfoList.stream().noneMatch(info -> info.getName().equals(finalParentCode))) {
                parentCode = DICT_CODE_MODULE;
            }
            list.add(new SystemDictBatchSaveRequest.Item(
                    moduleInfo.getDescription(),
                    moduleInfo.getName(),
                    null,
                    moduleInfo.getCode(),
                    parentCode,
                    customData
            ));
        }
        SystemDictBatchSaveRequest request = new SystemDictBatchSaveRequest();
        request.setRootName("系统模块");
        request.setValueType("CUSTOM");
        request.setMaxLevel(4);
        request.setCustomFields(List.of(
                new SystemDictBatchSaveRequest.DictCustomField("type", "模块类型", "STRING", false),
                new SystemDictBatchSaveRequest.DictCustomField("code", "模块编码", "NUMBER", false),
                new SystemDictBatchSaveRequest.DictCustomField("packageName", "模块包名", "STRING", false),
                new SystemDictBatchSaveRequest.DictCustomField("packageVersion", "模块包版本", "STRING", true)
        ));
        request.setRootCode(DICT_CODE_MODULE);
        request.setOperatorId(0L);
        request.setItems(list);
        return request;
    }

    private CompletableFuture<Void> runAsync(String name, Supplier<SystemDictBatchSaveRequest> supplier) {
        return CompletableFuture.runAsync(() -> {
            long l1 = System.currentTimeMillis();
            SystemDictBatchSaveRequest request = supplier.get();
            if (!ObjectUtils.isEmpty(request.getItems())) {
                dictClient.batchSave(request);
            }
            long l2 = System.currentTimeMillis();
            log.debug("上报[{}]完成,数量:{},耗时{}ms", name, request.getItems().size(), l2 - l1);
        }).exceptionally(e -> {
            log.error("上报[{}]异常", name, e);
            return null;
        });
    }

}
